using System;
using System.Collections.Generic;
using System.Text;
using Weazel.Math;
using System.IO;

namespace Weazel.Gezel.Simulator.Reference
{
    /// <summary>
    /// Used to generate a ValueChangeDump-file (.VCD)
    /// which can be viewed with an appropriate viewer -
    /// e.g. GTKWave or WaveVCD.
    /// </summary>
    class ValueChangeDump
    {
        /// <summary>
        /// Name of the file which will contain the dump
        /// when the simulation is over.
        /// </summary>
        private string filename;

        /// <summary>
        /// True iff the current set of signal values
        /// should be written to the VCD file.
        /// </summary>
        private bool changeFlag;

        private StreamWriter writer;
        
        /// <summary>
        /// Stores the values which are dumped to 
        /// the VCD file.
        /// </summary>
        private List<Integer> signalValues;
        
        public ValueChangeDump(string filename)
        {
            this.changeFlag = true;
            this.filename = filename;
            this.writer = new StreamWriter(filename, false);
            signalValues = new List<Integer>();
        }

        /// <summary>
        /// Checks if the value of a signal has changed as
        /// part of the simulation cycle.
        /// </summary>
        /// <param name="id">Index of signal to update</param>
        /// <param name="newValue">New value of signal</param>
        internal void Update(Symid signalIndex, Integer newValue)
        {
            Integer current = signalValues[signalIndex];
            if (!current.Equals(newValue))
            {
                changeFlag = true;
                current.ValueCopy(newValue);
            }
        }

        internal void Evaluate(long generation)
        {
            // Close header if simulation is just started
            if (generation == 0L)
                writer.WriteLine("$enddefinitions $end");

            // Check if theres any changes to dump
            if (!changeFlag)
                return;

            changeFlag = false;

            writer.WriteLine("#" + generation.ToString());
            for (int i = 0; i < signalValues.Count; i++)
                writer.WriteLine("b{0} {1}", signalValues[i].ToBinaryString(), (char)(i + 33));
        }

        /// <summary>
        /// Register a TracerBlock which should be included in the dump.
        /// </summary>
        /// <param name="signal">Signal to read values from.</param>
        /// <returns></returns>
        internal Symid RegisterTracer(AbstractSignal signal)
        {
            char vcdIdentifier = (char)(signalValues.Count + 33);
            writer.WriteLine("$var reg \t{0} {1}\t{2}  $end", signal.Width(), vcdIdentifier, signal.Name);

            Integer value = new Integer((int)signal.Width(), 0, signal.Signed() == 1 ? true : false);
            signalValues.Add(value);
            return signalValues.Count - 1;
        }

        /// <summary>
        /// Flushes the stream and closes it
        /// </summary>
        public void CloseStream()
        {
            if (writer != null)
            {
                writer.Flush();
                writer.Close();
                writer = null;
            }
        }
    }
}
